package server;

import server.handle.ClientHandler;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

public class TCPServer implements ClientHandler.ClientHandlerCallback{
    private final int port;
    private final File cachePath;
    private final ExecutorService forwordingThreadPoolExecutor;
    private ClientListener listener;
    private List<ClientHandler> clientHandlerList = new ArrayList<>();
    private Selector selector;
    private ServerSocketChannel server;

    public TCPServer(int port, File cachePath) {
        this.port = port;
        this.cachePath = cachePath;
        // 转发线程池
        this.forwordingThreadPoolExecutor = Executors.newSingleThreadExecutor();
    }

    public boolean start() {
        try {
            selecltor = Selector.open();
            this.server = ServerSocketChannel.open();
            // 设置非阻塞
            this.server.configureBlocking(false);
            // 绑定本地客户端
            this.server.socket().bind(new InetSocketAddress(port));
            // 注册客户端连接到达监听
            this.server.register(selector, SelectionKey.OP_ACCEPT)

            System.out.println("服务器信息：" + server.getLocalAddress().toString());
            // 启动客户端监听
            ClientListener listener = this.listener = new ClientListener();
            listener.start();
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    public void stop() {
        if (listener != null) {
            listener.exit();
        }

        CloseUtils.close(server);
        CloseUtils.close(selector);

        // 遍历的时候有线程安全问题
        synchronized (TCPServer.this) {
            for (ClientHandler clientHandler : clientHandlerList) {
                clientHandler.exit();
            }
            clientHandlerList.clear();
        }

        // 停止线程池
        forwordingThreadPoolExecutor.shutdownNow();

    }

    public synchronized void broadcast(String str) {
        for (ClientHandler clientHandler : clientHandlerList) {
            clientHandler.send(str);
        }
    }

    // 自身关闭的一个通知
    @Override
    public synchronized void onSelfClosed(ClientHandler handler) {
        clientHandlerList.remove(handler);
    }

    // 收到消息是通知
    @Override
    public void onNewMessageArrived(ClientHandler handler, String msg) {
        // 打印到屏幕上
        System.out.println("Received-"+handler.getClientInfo()+":"+msg);

        // 这里的实现要避免接受消息阻塞，所以需要考虑线程池
        // 异步提交转发任务
        forwordingThreadPoolExecutor.Execute(()->{
            synchronized (TCPServer.this) {
                for (ClientHandler clientHandler : clientHandlerList) {
                    if (clientHandler.equals(handler)) {
                        // 跳过自己
                        continue;
                    }
                    // 对其他客户端发送消息
                    clientHandler.send(msg);
                }
            }
        })
    }

    private class ClientListener extends Thread {
        private boolean done = false;

        @Override
        public void run() {
            super.run();

            Selector selector = TCPServer.this.selector;
            System.out.println("服务器准备就绪～");
            // 等待客户端连接
            do {
                try {
                    // 默认永远阻塞
                    if(selector.select() == 0) {
                        if(done){break;}
                        continue;
                    }

                    Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                    while (iterator.hasNext()) {
                        if (done) {
                            break;
                        }

                        SelectionKey key = iterator.next();
                        iterator.remove();

                        // 检查当前Key的状态是否是我们关注的
                        // 客户端到达状态
                        if(key.isAcceptable()) {
                            ServerSocketChannel serverSocketChannel = (ServerSocketChannel)key.channel();
                            // 非阻塞状态去拿到客户端连接
                            SocketChannel socketChannel = serverSocketChannel.accept();

                            try {
                                // 客户端构建异步线程
                                ClientHandler clientHandler = new ClientHandler(socketChannel, TCPServer.this, cachePath);
                                // 读取数据并打印
                                clientHandler.readToPrint();
                                synchronized (TCPServer.this) {
                                    clientHandlerList.add(clientHandler);
                                }
                            } catch (IOException e) {
                                e.printStackTrace();
                                System.out.println("客户端连接异常：" + e.getMessage());
                            }
                        }
                    }
                } catch (Exception exception) {
                    exception.printStackTrace();
                }

            } while (!done);

            System.out.println("服务器已关闭！");
        }

        void exit() {
            done = true;
            // 唤醒当前的阻塞
            selector.wakeUp();
        }
    }
}
